/**
 * 
 */
package br.org.bertol.mestrado.engine.fitness;

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;

import br.org.bertol.mestrado.engine.Objective;
import br.org.bertol.mestrado.engine.optimisation.entity.AbstractUnit;
import br.org.bertol.mestrado.engine.optimisation.moo.ParetoDominace;
import br.org.bertol.mestrado.engine.optimisation.moo.ParetoMinMaxDominace;

/**
 * @author contaqualquer
 */
public class MutantAnalysis extends AbstractCombinatorialFitness<String> {

    /**
     * Fronteira de Pareto.
     */
    private transient ParetoMinMaxDominace paretoDominace = new ParetoMinMaxDominace();

    /**
     * Teste case selecionados.
     */
    private transient List<String>         testCase       = new ArrayList<String>();

    public MutantAnalysis(final ArrayList<String> classes) {
        super(classes);
    }

    @Override
    public final boolean fitness(final List<String> route,
            final EnumMap<Objective, Number> height) {

        for (final Objective objective : height.keySet()) {

            switch (objective) {

                case MUTANTES:
                    height.put(objective, objective.getConstraintHeight(route));

                    testCase = objective.getInfo();

                    break;
                case COBERTURA:
                    height.put(objective, objective
                            .getConstraintHeight(testCase));

                    testCase = objective.getInfo();

                    break;
                default:
                    break;
            }
        }
        return true;
    }

    @SuppressWarnings("unchecked")
    @Override
    public final Object clone() throws CloneNotSupportedException {

        final MutantAnalysis tmp = (MutantAnalysis) super.clone();

        tmp.testCase = (List<String>) ((ArrayList<String>) this.testCase)
                .clone();

        return tmp;
    }

    @Override
    public final void reset() {
        testCase.clear();
    }

    @Override
    public final boolean stopCriteria() {
        return false;
    }

    @SuppressWarnings("unchecked")
    @Override
    public final <U extends AbstractUnit<String>> U doBetter(final U unit)
            throws Exception {

        final U tmp = (U) unit.clone();

        final int totalChanges = (int) (unit.getRoute().size() * Math.random());

        final double roulet = Math.random();

        for (int counter = 0; counter < totalChanges; counter++) {

            if (roulet <= 0.5) {
                TipoMovimento.ADICIONAR.execute(tmp, classes);
            } else if (0.5 < roulet) {
                TipoMovimento.SUBSTITUIR.execute(tmp, classes);
            }
        }

        if (tmp.getRoute().size() >= 2) {

            final EnumMap<Objective, Number> calc = tmp.getHeight().clone();

            final List<String> tmpL = (List<String>) tmp.getRoute().clone();

            for (final String operador : tmpL) {
                tmp.removeClass(operador);

                tmp.calcFitness();

                if (paretoDominace.compare(tmp.getHeight(), calc) != ParetoDominace.LEFT_DOMINE) {
                    tmp.addClass(operador);
                    tmp.calcFitness();
                }
            }
        }

        return tmp;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final String toString() {
        final StringBuilder buffer = new StringBuilder();

        buffer.append("Casos de teste : ").append(testCase.toString());

        return buffer.toString();
    }

    /**
     * Tipos de movimentos para o problema de mutação.
     * @author contaqualquer
     */
    private enum TipoMovimento {

        /***/
        ADICIONAR {
            /**
             * Executa movimento.
             * @param unit
             *            Unidade
             * @param classes
             *            Lista de classes
             */
            @SuppressWarnings("unchecked")
            protected void execute(final AbstractUnit<String> unit,
                    final ArrayList<String> classes) {

                final ArrayList<String> reducedClasses =
                        (ArrayList<String>) classes.clone();

                reducedClasses.removeAll(unit.getRoute());

                if (!reducedClasses.isEmpty()) {

                    final int pointer = (int) Math
                            .floor((reducedClasses.size() * Math
                            .random()));

                    unit.addClass(reducedClasses.get(pointer));
                }
            }

        },
                /***/
        SUBSTITUIR {

            @SuppressWarnings("unchecked")
            protected void execute(final AbstractUnit<String> unit,
                    final ArrayList<String> classes) {

                final ArrayList<String> reducedClasses =
                        (ArrayList<String>) classes.clone();

                reducedClasses.removeAll(unit.getRoute());

                if (!reducedClasses.isEmpty()) {
                    final int pointer1 = (int) (unit.getRoute().size() * Math
                            .random());

                    final int pointer2 = (int) (reducedClasses.size() * Math
                            .random());

                    unit.removeClass(pointer1);

                    unit.addClass(reducedClasses.get(pointer2));
                }
            }
        };

        protected abstract void execute(final AbstractUnit<String> unit,
                final ArrayList<String> classes);
    };

}
